//
//  RexelHelpers.cpp
//  Rexel
//
//  Created by Zach Zahos on 2014-05-19.
//  Copyright (c) 2014 Zach Zahos. All rights reserved.
//

#include <stdlib.h>
#include <string.h>
#include "RexelHelpers.h"

int contains(const char *buffer, unsigned long length, char chr) {
	for (int i = 0; i < length; i++) {
		if (buffer[i] == chr) {
			return i;
		}
	}
	return -1;
}

RXLValidity RXLTypeDefCharacters(const char *buffer, unsigned long index, void *settings, void **context) {
	RXLTypeDefCharactersSettings *sets = settings;
	char chr = *(buffer+index);
	if (index == 0 && sets->validPrefixes != NULL && contains(sets->validPrefixes, sets->prefixes, chr) < 0) {
		return RXLValidityInvalid;
	} else if (contains(sets->validCharacters, sets->characters, chr) > -1) {
		return RXLValidityValid;
	} else if (index == 0) {
		return RXLValidityInvalid;
	}
	return RXLValidityComplete;
}

typedef struct {
	RXLTypeDefStringQuoteSettings **matching;
	unsigned check;
	long lastMatching, lastValid;
	RXLTypeDefStringQuoteSettings *quote;
	RXLBool escaped;
} RXLTypeDefStringContext;
RXLValidity RXLTypeDefString(const char *buffer, unsigned long index, void *settings, void **context) {
	RXLTypeDefStringSettings *sets = settings;
	RXLTypeDefStringContext *details = *context;
	if (details == NULL) {
		details = malloc(sizeof(RXLTypeDefStringContext));
		details->matching = malloc(sizeof(RXLTypeDefStringQuoteSettings*) * sets->quotes);
		memcpy(details->matching, sets->quoteSettings, sizeof(RXLTypeDefStringQuoteSettings*) * sets->quotes);
		details->lastMatching = sets->quotes-1;
		details->lastValid = -1;
		details->check = 0;
		details->quote = NULL;
		details->escaped = RXLFalse;
		*context = details;
	}
	char chr = *(buffer+index);
	RXLValidity validity = RXLValidityIncomplete;
	if (details->quote == NULL) {
		for (int i = 0; i <= details->lastMatching;) {
			RXLTypeDefStringQuoteSettings *cur = details->matching[i];
			if (chr != cur->prefix[details->check]) {
				details->matching[i] = details->matching[details->lastMatching];
				if (details->lastValid >= 0) {
					details->matching[details->lastMatching] = details->matching[details->lastValid];
					details->lastValid--;
				}
				details->lastMatching--;
			} else if (details->check == cur->prefixLength-1) {
				details->matching[i] = details->matching[details->lastMatching];
				details->matching[details->lastMatching] = cur;
				if (details->lastValid < 0) {
					details->lastValid = details->lastMatching;
				}
				details->lastMatching--;
			} else {
				i++;
			}
		}
		details->check++;
		if (details->lastMatching == -1) {
			details->check = 0;
			if (details->lastValid == -1) {
				validity = RXLValidityInvalid;
			} else {
				unsigned long len = 0;
				for (int i = 0; i <= details->lastValid; i++) {
					RXLTypeDefStringQuoteSettings *cur = details->matching[i];
					if (cur->prefixLength > len) {
						len = cur->prefixLength;
						details->quote = cur;
					}
				}
			}
			free(details->matching);
			details->matching = NULL;
		}
	} else if (chr == sets->escapeCharacter) {
		details->escaped = !details->escaped;
	} else if (details->escaped) {
		details->escaped = RXLFalse;
	} else {
		if (chr == details->quote->suffix[details->check]) {
			details->check++;
			if (details->check == details->quote->suffixLength) {
				validity = RXLValidityComplete;
			}
		} else {
			details->check = 0;
		}
		if ((chr == '\r' || chr == '\n') && !details->quote->multiline) {
			validity = RXLValidityInvalid;
		}
	}
	if (validity == RXLValidityInvalid || validity == RXLValidityComplete) {
		free(details);
		*context = NULL;
	}
	return validity;
//	if (index == 0) {
//		if (contains(sets->delimiterCharacters, sets->delimiters, chr) == -1) {
//			return RXLValidityInvalid;
//		}
//		details = malloc(sizeof(RXLTypeDefStringContext));
//		details->delimiter = chr;
//		details->escaped = RXLFalse;
//		*context = details;
//	} else if (contains(sets->escapeCharacters, sets->escapes, chr) > -1) {
//		details->escaped = !details->escaped;
//	} else {
//		if (chr == details->delimiter && !details->escaped) {
//			free(details);
//			*context = NULL;
//			return RXLValidityComplete;
//		}
//		details->escaped = RXLFalse;
//	}
//	return RXLValidityIncomplete;
}

typedef struct {
	RXLBool decimal, exponent, hasDigits;
	char base;
} RXLTypeDefNumberContext;
RXLValidity RXLTypeDefNumber(const char *buffer, unsigned long index, void *settings, void **context) {
	RXLTypeDefNumberSettings *sets = settings;
	RXLTypeDefNumberContext *details = *context;
	RXLTypeDefNumberBaseSettings *base = sets->validBases[0];
	if (details && details->base != 0) {
		for (int i = 0; i < sets->bases; i++) {
			RXLTypeDefNumberBaseSettings *cur = sets->validBases[i];
			if (cur->base == details->base) {
				base = cur;
				break;
			}
		}
	}
	char chr = *(buffer+index);
	RXLValidity validity = RXLValidityInvalid;
	if (index == 0) {
		RXLBool hasDigits = RXLFalse;
		if ((chr == '+' && sets->allowPositive) || (chr == '-' && sets->allowNegative) || (chr == '.' && base->allowDecimal)) {
			validity = RXLValidityIncomplete;
		} else if (contains(base->validDigits, base->digits, chr) > -1) {
			hasDigits = RXLTrue;
			validity = RXLValidityValid;
		}
		if (validity != RXLValidityInvalid) {
			details = malloc(sizeof(RXLTypeDefNumberContext));
			memset(details, 0, sizeof(RXLTypeDefNumberContext));
			details->decimal = (chr == '.');
			details->hasDigits = hasDigits;
			*context = details;
		}
	} else {
		if (index == 1 && sets->validBases && *buffer == '0') {
			for (int i = 0; i < sets->bases; i++) {
				if (chr == sets->validBases[i]->base) {
					details->base = chr;
					validity = RXLValidityValid;
				}
			}
		}
		if (validity != RXLValidityValid) {
			validity = RXLValidityComplete;
			if (contains(base->validDigits, base->digits, chr) > -1) {
				details->hasDigits = RXLTrue;
				validity = RXLValidityValid;
			} else if (chr == '.') {
				if ((!base->allowDecimal || details->decimal) && !details->hasDigits) {
					validity = RXLValidityInvalid;
				} else {
					validity = RXLValidityValid;
				}
			}
		}
	}
	if ((validity == RXLValidityInvalid || validity == RXLValidityComplete) && details) {
		free(details);
		*context = NULL;
	}
	return validity;
}
